闲话
啊这场要做A~D,遗憾的是我BC都差点没搞完,特别是C居然得上就很蛋疼,到最后都没时间看D了.做题的速度还是有待提高.
A. Distinct Digits
题目大意:给定两个整数要求输出在之间的一个各个数位都不同的数.输出一个即可,无解输出-1.
数据范围:
思路
数据范围较小,直接暴力即可.
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(int x)
{
int st[10] = {0};
while(x)
{
st[x % 10] ++;
x /= 10;
}
for(int i = 0;i < 10;++i)
if(st[i] > 1)
return 0;
return 1;
}
int main()
{
int l,r;scanf("%d%d",&l,&r);
for(int i = l;i <= r;++i)
{
if(check(i))
{
printf("%d",i);
return 0;
}
}
printf("-1");
return 0;
}
B. Filling the Grid
题目大意:有一个的格子纸,给每一行每一列都规定了一个值,表示跟边界相连的连续的几个块必须要被染色.问在满足前提条件下有多少种不同的染色方案,对取模.
思路
对答案的统计来说,应该是整个格子纸里可以任意选择颜色的格子才有计算的必要,对于一个可以任意染色的格子,他有种选择,因此这个题目就是要求出有多少个格子是可以任意染色的,设为.那么答案就是,显然写一个快速幂就可以轻松解决.
考虑怎么计算可以任意染色的格子的数量.显然不可能一个一个的把一行一列丢进去考虑,先假定所有的列都已经加进去了,再一个一个加行,计算新加入的行里有多少个是不受影响的,累加即可.可以顺带判断是否有矛盾的地方.
对于行而言,他这一行的颜色是确定的,因为前个是确定被染色,而是必须不染色的.因此判断是否违法的条件就是看最后一列是否是强制被染色的即可,条件判断就是先找到那一列,然后对那一列来说,最后的一位是否覆盖到了第行的范围,如果是的话就无解.对于列的情况也同理,对称处理即可.而统计答案的话,就遍历对到边界的格子,看这一列上面往下伸是不是已经到了可以任意选择的位置,如果是,就累加.
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3+7,MOD = 1e9+7;
int r[N],c[N];
int qpow(int a,int b)
{
int res = 1 % MOD;
while(b)
{
if(b & 1) res = (ll)res * a % MOD;
a = (ll)a * a % MOD;
b >>= 1;
}
return res;
}
int main()
{
int h,w;scanf("%d%d",&h,&w);
for(int i = 1;i <= h;++i) scanf("%d",&r[i]);
for(int i = 1;i <= w;++i) scanf("%d",&c[i]);
int ok = 1;
for(int i = 1;i <= h;++i)
if(c[r[i] + 1] >= i)
{
ok = 0;
break;
}
for(int i = 1;i <= w;++i)
if(r[c[i] + 1] >= i)
{
ok = 0;
break;
}
if(!ok) puts("0");
else
{
int res = 0;
for(int i = 1;i <= h;++i)
{
for(int j = r[i] + 2;j <= w;++j)
{
if(i >= c[j] + 2)
++res;
}
}
printf("%d\n",qpow(2,res));
}
return 0;
}
C. Primes and Multiplication
题目大意:
定义函数得到的是的所有的质因数的集合.
定义函数的结果是最大的且.
定义函数的结果是所有的的的结果的乘积.
数据范围:
思路
这题数据范围大的离谱,从范围上入手,应该是一个的算法,大概的也就筛一下里的所有的质因数.而且不能说遍历别的跟有关的内容.
观察形式可以发现重复元素比较多,且和质因数有关,考虑贡献:
假定现在在枚举的质因数是,则每次都要枚举的若干倍,记作.对函数而言,实际上就是在求在里面一共有几次,出现了次最后的结果就是.则对于对整个答案的影响,单个的的贡献是,而整个序列里一共要出现次.原因是整个序列是相乘的形式,所以幂就是相加的形式,最终的答案幂次是整个序列里出现的次数.
分别统计即可.
注意会爆long long,套unsigned long long解决.
代码:
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
#define int ll
vector<ll> p;
vector<vector<ll>> fact;
const int N = 2e6+7,MOD = 1e9+7;
int primes[N],cnt,st[N];
ll qpow(ll a,ll b)
{
ll res = 1 % MOD;
while(b)
{
if(b & 1) res = (ll)res * a % MOD;
a = (ll)a * a % MOD;
b >>= 1;
}
return res;
}
void init()
{
for(int i = 2;i < N;++i)
{
if(!st[i]) primes[cnt++] = i;
for(int j = 0;primes[j] * i < N;++j)
{
st[primes[j] * i] = 1;
if(i % primes[j] == 0) break;
}
}
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
init();
ll x,n;cin >> x >> n;
for(int i = 0;i < cnt;++i)
{
if(x % primes[i] == 0)
{
p.push_back(primes[i]);
while(x % primes[i] == 0) x /= primes[i];
}
}
if(x > 1) p.push_back(x);
ll res = 1;
for(int i = 0;i < p.size();++i)
{
for(ll curp = p[i];curp <= n;curp *= p[i])
{
res = (res * qpow(p[i],n / curp)) % MOD;
if(curp > n / p[i]) break;
}
}
cout << res << endl;
return 0;
}
D. Complete Tripartite
题目大意:给定一个个点条边的无向图.现要求把整个图上的点分成三类集合,每个集合没有交集,并且要使任意两个集合都是牛逼的.定义两个集合是牛逼的当且仅当两个集合没有交集且集合内部的所有点之间没有边相连,而两个集合之间的每个点都有边相连.要求构造一种方案,如果有多个输出一个即可.
数据范围:
思路
如果图不连通,则无解.
由于只要输出一种方案,不妨贪心的尝试构造.
首先对于一个点来说,如果他的集合已经确定了,那么与他直接相连的点必然来自于其他的集合.因此可以先钦定点是分配到集合的,那么对于与直接相连的所有点来说他们就必然来自或者集合,而没有的就都属于集合.在这些直接相连的点的集合中任意选出一个点,把他的颜色染成并继续往外标记,那么遍历到的点如果满足是之前被拓展到的点的话,就认为是属于集合的,剩余的还没被标记的就一定是集合的了.
考虑怎么判断是否合法,由于题目的条件特别强,就是说这个题目的条件他是使得对任意一个点来说,所有与它不属于一个集合的点都必须与它联通.从这个特别强的性质入手比较好.
记三种集合的点数分别为显然三者之和的条件一定是满足的.
①考虑边数的条件,由于题目保证每两个点之间最多也就一个边相连,可以得到一个表达式:这是因为之前所说的,对于集合里的节点来说,所有集合的点和节点的点必然与之相连,所以与点产生的边数就是,其他同理,补上即可.
②对每个点来说与它相连接的点必然不能是同色的.
③对于每种颜色的节点来说,他的数量不能是.
以上性质遍历验证即可.
本题有些细节问题比较细微,建议看代码的时候注意一下.最后还有一个实现上的问题:因为这张无向图是可能带有环的,所以普通的带有的可能也还是跑的非常慢,甚至应该会死循环.对这种情况必须要加一个数组进行判断,这一点是不能省掉的.
最后还是提一下细节:
号点是第一个点,他所在的集合是.在遍历完与他相连的点之后,剩下的没有遍历到的点就是的.钦定的点是与它相连的随便的一个点,它的所在的集合是.与它相连的且与相连的就是.注意不要把已经有颜色的点给覆盖掉了.以及号点的标记也是要打的,不打的话会误认为不是已经在集合里的点.
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7,M = 1e6+7;
int edge[M],succ[M],ver[N],idx;
int n,m,connected[N],col[N];
int st[N],ok = 1,fnlvis[N];
void add(int u,int v)
{
edge[idx] = v;
succ[idx] = ver[u];
ver[u] = idx++;
}
void dfs(int u,int fa)
{
connected[u] = 1;
for(int i = ver[u];~i;i = succ[i])
{
int v = edge[i];
if(v == fa || connected[v]) continue;
dfs(v,u);
}
}
void dfs2(int u,int fa)
{
fnlvis[u] = 1;
for(int i = ver[u];~i;i = succ[i])
{
int v = edge[i];
if(v == fa || fnlvis[v]) continue;
if(col[v] == col[u]) ok = 0;
dfs2(v,u);
}
}
int main()
{
memset(ver,-1,sizeof ver);
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++i)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1,-1);
for(int i = 1;i <= n;++i)
if(!connected[i])
return puts("-1"),0;
col[1] = 1;st[1] = 1;
for(int i = ver[1];~i;i = succ[i])
{
int v = edge[i];
st[v] = 1;
}
for(int i = 1;i <= n;++i)
if(!st[i])
col[i] = 1;
int v = edge[ver[1]];
col[v] = 2;
// cout << v << endl;
for(int i = ver[v];~i;i = succ[i])
{
int u = edge[i];
if(!col[u] && st[u])
col[u] = 3;
}
// for(int i = 1;i <= n;++i)
// cout << col[i] << " ";cout << endl;
int cnt[4] = {0};
for(int i = 1;i <= n;++i)
{
if(!col[i]) col[i] = 2;
++cnt[col[i]];
}
// cout << cnt[1] << " " << cnt[2] << " " << cnt[3] << endl;
if(!cnt[1] || !cnt[2] || !cnt[3]) return puts("-1"),0;
if(cnt[1] * (cnt[2] + cnt[3]) + cnt[2] * cnt[3] != m) return puts("-1"),0;
for(int u = 1;u <= n;++u)
{
int c[4] = {0};
for(int i = ver[u];~i;i = succ[i])
{
int v = edge[i];
++c[col[v]];
}
if(c[col[u]] != 0) return puts("-1"),0;
if(col[u] == 1 && c[2] != cnt[2] && c[3] != cnt[3]) return puts("-1"),0;
if(col[u] == 2 && c[1] != cnt[1] && c[3] != cnt[3]) return puts("-1"),0;
if(col[u] == 3 && c[2] != cnt[2] && c[1] != cnt[1]) return puts("-1"),0;
}
for(int i = 1;i <= n;++i)
printf("%d ",col[i]);
return 0;
}